/*
 * Partida.cpp
 *
 * Author: Grupo Nro.
 */

#include "Partida.h"
#include "Game.h"
#include <iostream>
#include "Message.h"
#include "Mutex.h"
#include "Utils.h"

using std::string;
using std::vector;
using std::map;

Partida::Partida(int id, string nombre, JugadorConectado& jugadorCreador) {
	this->id = id;
	this->nombre = nombre;
	this->jugador_creador = &jugadorCreador;
	this->estado = SIN_INICIAR;
	this->campaign = 0;
	this->level_number = 1;
	current_game = 0;
}

Partida::~Partida() {
	players.clear();
	if (current_game) {
	    delete current_game;
	}
}

int Partida::getId(){
	return this->id;
}

int Partida::getCantJugadores(){
	return this->players.size();
}

int Partida::getEstado(){
	return this->estado;
}

void Partida::setCampaign(Campania& campaign) {
    this->campaign = &campaign;
}

Campania* Partida::getCampania() {
    return this->campaign;
}

int Partida::getLevelNumber() {
    return level_number;
}

JugadorConectado* Partida::getJugadorCreador(){
	return this->jugador_creador;
}

void Partida::addPlayer(JugadorConectado& player){
	this->players.push_back(&player);
	scores[player.getId()] = 0;
}

vector<JugadorConectado*>& Partida::getPlayers(){
	return this->players;
}

void Partida::resetScores() {
    map<int, int>::iterator scores_it;
    for (scores_it = scores.begin(); scores_it != scores.end(); ++scores_it) {
        Message message;
        message.code = SCORES;
        message.args.push_back(Utils::intToString(0));
        notifyPlayer(scores_it->first, message);
        scores_it->second = 0;
    }
}

void Partida::addScore(int player_id, int points) {
    scores[player_id] += points;
    Message message;
    message.code = SCORES;
    message.args.push_back(Utils::intToString(scores[player_id]));
    notifyPlayer(player_id, message);
}

int Partida::getScore(int player_id) {
    if (scores.find(player_id) != scores.end()) {
        return scores[player_id];
    }
    return -1;
}

map<int, int>& Partida::getScores() {
    return scores;
}

void Partida::comenzarPartida(){
	estado = INICIADA;
	start();
}

void Partida::run() {
    while (isRunning()) {
        Lock l(m);
        setUpGame();
        current_game->startGame();
        GameState game_state = current_game->getState();
        if (game_state == WON) {
            level_number++;
        } else if (game_state == LOST){
            resetScores();
            level_number/= 2;
            if (level_number == 0) {
                level_number = 1;
            }
        } else {
//            interrumpirPartida();
            break;
        }
        std::cout << "Partida " << id << ": Numero de nivel a cargar = " << level_number << std::endl;
        if (level_number > campaign->getNumberOfLevels()) {
            //Campaing won!
            finalizarPartida();
        } else {
            Message message;
            message.code = END_MATCH;
            notifyPlayers(message);
            l.wait();
        }
    }
}

void Partida::interrumpirPartida() {
    if (estado != FINALIZADA) {
        std::cout << "Se interrumpió la partida" << std::endl;
        resetScores();
        finalizarPartida();
    }
}

void Partida::finalizarPartida(){
    if (current_game != 0) {
        current_game->stopGame();
        stop();
        join();
//        delete current_game;
//        current_game = 0;
    }
    Message message;
    message.code = END_CAMPAIGN;
    notifyPlayers(message);
	estado = FINALIZADA;
}

void Partida::notifyPlayer(int player_id, Message msg) {
    vector<JugadorConectado*>::iterator it;
    for (it = players.begin(); it != players.end(); ++it) {
        if ((*it)->getId() == player_id) {
            (*it)->enqueueEventMessage(msg);
            break;
        }
    }
}

void Partida::notifyPlayers(Message msg) {
    vector<JugadorConectado*>::iterator it;
    for (it = players.begin(); it != players.end(); ++it) {
        (*it)->enqueueEventMessage(msg);
    }
}

void Partida::setId(int id) {
	this->id = id;
}

void Partida::setUpGame() {
    if (current_game != 0) {
        delete current_game;
    }
    Nivel* level = campaign->getLevel(level_number);
    current_game = new Game(*this, *level);
    current_game->loadLevel();
}

std::string Partida::getNombre() {
	return nombre;
}

bool Partida::agregarJugadorNuevo(JugadorConectado* jc) {
	size_t minimo = campaign->getCantJugadoresMin();
	players.push_back(jc);
	if (players.size() >= minimo) {
		return true;
	} else {
		return false;
	}
}

bool Partida::solicitarComienzoDePartidaACreadorDePartida() {
	return jugador_creador->solicitarComienzoDePartida();
}

bool Partida::moveUp(int player_id) {
    return current_game->moveUp(player_id);
}

bool Partida::moveRight(int player_id) {
    return current_game->moveRight(player_id);
}

bool Partida::moveDown(int player_id) {
    return current_game->moveDown(player_id);
}

bool Partida::moveLeft(int player_id) {
    return current_game->moveLeft(player_id);
}

bool Partida::plantBomb(int player_id) {
    return current_game->plantBomb(player_id);
}

const string& Partida::getImageFileName() const {
	return imageFileName;
}

void Partida::setImageFileName(const string& imageFileName) {
	this->imageFileName = imageFileName;
}

string& Partida::getLevelMapTileRepresentation() {
    Nivel* level = campaign->getLevel(level_number);
    return level->getTileMapRepresentation();
}

void Partida::signalStartNextLevel() {
    Lock l(m);
    l.signal();
}

bool Partida::playerCanJoin() {
    std::size_t campaign_players = campaign->getCantJugadoresMin();
    return ((estado == SIN_INICIAR) && (players.size() < campaign_players));
}
